package xml.parseur;

import graphviz.MetaModel.ElementDot;
import graphviz.MetaModel.LiaisonDot;
import graphviz.MetaModel.LigneElementDot;
import graphviz.MetaModel.enumeration.TypeElementDot;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import languageObject.metatModel.enumeration.TypeElementObject;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import partager.TypeSousElement;
import partager.TypeSousElementVar;

import utils.StringUtils;
import utils.Utils;
import xml.LecteurXml;
import constantes.ConstanteXml;

/**
 * permet de lire le fichier xml et d'en extraire le metamodel DOT
 */
public class XmlGraphviz implements LecteurXml<ElementDot>
{
	private File file;
	
	/**
	 * constructeur par recopie
	 */
	public XmlGraphviz(File file)
	{
		this.file = file;
	}
	
	@Override
	public List<ElementDot> lecture()
	{
		List<ElementDot> list = new ArrayList<ElementDot>();
		
		if(this.file == null) return list;
		
	    try 
	    {	 
	    	DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
	    	DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
	    	Document doc = dBuilder.parse(this.file);
	    	doc.getDocumentElement().normalize();

	    	//le package, on ne le compte pas
	    	
	    	//recupere la liste des elements.
	    	NodeList listElement = doc.getElementsByTagName(ConstanteXml.ELEMENT);
	    	if(listElement == null) return null;
	    	for (int i = 0; i < listElement.getLength(); i++) 
	    	{
	    		ElementDot element = new ElementDot();
	    		
	    		Node nElement = listElement.item(i);
	    		Element eElement = (Element) nElement;
	    		
/***************************************************************************************************/
/*********************le recuperation du type de l'element racine.**********************************/
/***************************************************************************************************/
	    		String type = eElement.getAttribute(ConstanteXml.ELEMENT_TYPE);
	    		if(Utils.equals(TypeElementObject.INTERFACE.getnum(), type)) element.setType(TypeElementDot.INTERFACE);
	    		else if(Utils.equals(TypeElementObject.ENUM.getnum(), type)) element.setType(TypeElementDot.ENUM);
	    		else element.setType(TypeElementDot.CLASS);
	    		
/***************************************************************************************************/
/*********************le recuperation du nom de l'element.******************************************/
/***************************************************************************************************/
	    		element.setTitre(eElement.getAttribute(ConstanteXml.ELEMENT_NOM));
	    		
/***************************************************************************************************/
/*********************le recuperation des attributs de l'element.***********************************/
/***************************************************************************************************/
		    	NodeList listAttribut = eElement.getElementsByTagName(ConstanteXml.ATTRIBUT);
		    	if(listAttribut != null)
		    	{
			    	for(int j = 0; j < listAttribut.getLength(); ++j)
			    	{
			    		LigneElementDot attribut = new LigneElementDot();
			    		//type attribut
			    		attribut.setType(TypeSousElement.ATTRIBUT);
			    		
			    		Node nSousElementAttribut = listAttribut.item(j);
			    		Element eSousElementAttribut = (Element) nSousElementAttribut;
			    	
			    		//le nom de l'attribut
			    		attribut.setNom(eSousElementAttribut.getAttribute(ConstanteXml.ATTRIBUT_NOM));
	
			    		// le type de l'attribut
			    		attribut.setOutput(typeVar(eSousElementAttribut));
			    		
			    		element.add(attribut);
			    	}
		    	}
		    	
/***************************************************************************************************/
/*********************le recuperation des methodes de l'element.************************************/
/***************************************************************************************************/
		    	NodeList listMethode = eElement.getElementsByTagName(ConstanteXml.METHODE);
		    	if(listMethode != null)
		    	{
			    	for(int j = 0; j < listMethode.getLength(); ++j)
			    	{
			    		LigneElementDot methode = new LigneElementDot();
			    		//type methode
			    		methode.setType(TypeSousElement.METHODE);
			    		
			    		Node nSousElementMethode = listMethode.item(j);
			    		Element eSousElementMethode = (Element) nSousElementMethode;
			    		
			    		//le nom de la methode
			    		methode.setNom(eSousElementMethode.getAttribute(ConstanteXml.METHODE_NOM));
			    		
			    		// le type de sortie
				    	NodeList tmpTypeVar = eSousElementMethode.getElementsByTagName(ConstanteXml.METHODE_OUTPUT);
			    		methode.setOutput(typeVar((Element)tmpTypeVar.item(0)));
		    		
			    		// les entrees
				    	NodeList listEntreeMethode = eSousElementMethode.getElementsByTagName(ConstanteXml.METHODE_INPUT);
				    	if(listEntreeMethode != null)
				    	{
					    	for(int k = 0; k < listEntreeMethode.getLength(); ++k)
					    	{
					    		TypeSousElementVar sousElementMethodeInput = new TypeSousElementVar();
					    		
					    		Node nSousElementMethodeInput = listEntreeMethode.item(k);
					    		Element eSousElementMethodeInput = (Element) nSousElementMethodeInput;
					    		
					    		if(eSousElementMethodeInput != null)
					    		{		    		
						    		//le type de retour.
					    			
					    			NodeList eTypeVar = eSousElementMethodeInput.getElementsByTagName(ConstanteXml.METHODE_INPUT_NB);
					    			if(eTypeVar.item(0) == null) return null;
					    			String tmpType = eTypeVar.item(0).getTextContent();
					    			sousElementMethodeInput.setList(StringUtils.IsList(tmpType));
					    			
					    			eTypeVar = eSousElementMethodeInput.getElementsByTagName(ConstanteXml.METHODE_INPUT_TYPE);
					    			if(eTypeVar.item(0) == null) return null;
					    			String tmpNom = eTypeVar.item(0).getTextContent();
					    			sousElementMethodeInput.setNom(tmpNom);
					    			
					    			
						    		methode.add(sousElementMethodeInput);
					    		}
					    	}
				    	}
			    		element.add(methode);
			    	}
		    	}
		    			    	
/***************************************************************************************************/
/*********************le recuperation de la note de l'element.**************************************/
/***************************************************************************************************/
		    	NodeList listNote = eElement.getElementsByTagName(ConstanteXml.NOTE);
		    	if(listNote != null && listNote.getLength() > 0) element.setNote(listNote.item(0).getTextContent());

		    	list.add(element);
	    	}
	    	
	    	
/***************************************************************************************************/
/*********************le recuperation des laiaisons.************************************************/
/***************************************************************************************************/
	    	NodeList listliaisons = doc.getElementsByTagName(ConstanteXml.ASSOCIATION);
	    	if(listliaisons != null)
	    	{
		    	for(int i = 0; i < listliaisons.getLength(); ++i)
		    	{
		    		LiaisonDot liaison = new LiaisonDot();
		    		String queue = null;
		    		String tete = null;
		    		
		    		Node nLiaison = listliaisons.item(i);
		    		Element eLiaison = (Element) nLiaison;
		    		
		    		//recupere le type de la liaison
		    		Utils.isTypeLiaison(eLiaison.getAttribute(ConstanteXml.ASSOCIATION_TYPE), liaison);
		    		
		    		//recupere la queue de la liaison
		    		NodeList tmp = eLiaison.getElementsByTagName(ConstanteXml.ASSOCIATION_QUEUEFLECHE);
		    		if(tmp != null && tmp.getLength() != 0)  queue = tmp.item(0).getTextContent();
		
		    		//recupere la tete de la liaison
		    		tmp = eLiaison.getElementsByTagName(ConstanteXml.ASSOCIATION_FLECHE);
		    		if(tmp != null && tmp.getLength() != 0)  tete = tmp.item(0).getTextContent();
		    		
		    		ElementDot elementFils = null;
		    		
		    		//recherche de la tete et de la queue
		    		for(int j = 0; j < list.size(); ++j)
		    		{
		    			//si queue trouver on la sauvegarde
		    			if(list.get(j).getTitre().equalsIgnoreCase(tete))
		    			{
		    				elementFils = list.get(j);
		    			}
		    			//si tete trouver on la met dans la liaison
		    			if(list.get(j).getTitre().equalsIgnoreCase(queue))
		    			{
		    				liaison.setPere(list.get(j));
		    			}
		    			//si les deux trouvé on arret
		    			if(elementFils != null && liaison.getPere() != null) j = list.size();
		    		}
		    		
		    		tmp = eLiaison.getElementsByTagName(ConstanteXml.ASSOCIATION_CARDINALITE);
		    		if(tmp != null && tmp.getLength() != 0) liaison.setValLiaison(tmp.item(0).getTextContent());
		    			    		
		    		elementFils.add(liaison);
		    	}
	    	}
	    }
	    catch (Exception e){e.printStackTrace();}
		
		return list;
	}
	
	/**
	 * permet de sortir le type de l'objet
	 * @param eSousElement l'element
	 * @return le type de l'objet
	 */
	private TypeSousElementVar typeVar(Element eSousElement) 
	{
		if(eSousElement == null) return null;
				
		TypeSousElementVar typeVar = new TypeSousElementVar();
		NodeList eTypeVar = eSousElement.getElementsByTagName(ConstanteXml.ATTRIBUT_OUTPUT);
		if(eTypeVar.item(0) == null) return null;
		String tmpNom = eTypeVar.item(0).getTextContent();
		if(eTypeVar != null && eTypeVar.getLength() != 0) typeVar.setNom(tmpNom);
		NodeList tmp = eSousElement.getElementsByTagName(ConstanteXml.ATTRIBUT_OUTPUT_NB);
		if(tmp != null && tmp.getLength() != 0) typeVar.setList(StringUtils.IsList(tmp.item(0).getTextContent()));		    		
		
		return typeVar;
	}
}
